﻿using Linh.Mvvm;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ImageProcessing
{
    public class MainWindowViewModel : BindableBase
    {
        private int _Mode;
        private BitmapSource _OriginalSource;
        private BitmapSource _ProcessedSource;
        private int size;
        private int threshold = 40;

        public ICommand BrowseCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    OpenFileDialog dialog = new OpenFileDialog();
                    if (dialog.ShowDialog() == true)
                    {
                        var path = dialog.FileName;
                        OriginalSource = new BitmapImage(new Uri(path, UriKind.RelativeOrAbsolute));
                        //OriginalSource = ConvertToGrayscale(OriginalSource);
                        ProcessedSource = OriginalSource;
                    }
                });
            }
        }

        public int Mode
        {
            get { return _Mode; }
            set { SetProperty(ref _Mode, value); }
        }

        public BitmapSource OriginalSource
        {
            get { return _OriginalSource; }
            set { SetProperty(ref _OriginalSource, value); }
        }

        public ICommand ProcessCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    switch (Mode)
                    {
                        case 0:
                            ProcessedSource = MedianFilter(ProcessedSource, size + 1);
                            break;

                        case 1:
                            ProcessedSource = LinearFilter(ProcessedSource, size + 1);
                            break;

                        case 2:
                            ProcessedSource = OutlierFilter(ProcessedSource, size + 1, threshold);
                            break;
                    }
                });
            }
        }

        public BitmapSource ProcessedSource
        {
            get { return _ProcessedSource; }
            set { SetProperty(ref _ProcessedSource, value); }
        }

        public ICommand ResetCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    ProcessedSource = OriginalSource;
                });
            }
        }

        public ICommand OpenNewWindowCommand
        {
            get
            {
                return new DelegateCommand<object>((param) =>
                {
                    ResultWindow window = new ResultWindow();
                    StringBuilder sb = new StringBuilder();
                    if (ProcessedSource == OriginalSource)
                        sb.Append("Ảnh gốc");
                    else
                    {
                        switch (Mode)
                        {
                            case 0:
                                sb.Append("Lọc trung vị ");
                                break;

                            case 1:
                                sb.Append("Lọc trung bình ");
                                break;

                            case 2:
                                sb.Append("Lọc ngoài ");
                                break;
                        }
                        sb.Append(2 * size + 3).Append('x').Append(2 * size + 3);
                        if (Mode == 2)
                            sb.Append(" ngưỡng ").Append(threshold);
                    }
                    window.DataContext = new ResultWindowViewModel()
                    {
                        Description = sb.ToString(),
                        Image = ProcessedSource,
                    };
                    window.Show();
                });
            }
        }

        public int Size
        {
            get { return size; }
            set { SetProperty(ref size, value); }
        }

        public int Threshold
        {
            get { return threshold; }
            set { SetProperty(ref threshold, value); }
        }

        public BitmapSource OutlierFilter(BitmapSource image, int size = 1, int threshold = 40)
        {
            ImageHelper helper = new ImageHelper(image);
            ImageHelper output = new ImageHelper(image);
            int total;
            int sum = 0;
            var dimensions = image.Format.BitsPerPixel / 8;
            for (int dimension = 0; dimension < dimensions; dimension++)
            {
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        total = 0; sum = 0;
                        for (int u = -size; u <= size; u++)
                        {
                            for (int v = -size; v <= size; v++)
                            {
                                if (helper[i - u, j - v, dimension] != null)
                                {
                                    total += helper[i - u, j - v, dimension].Value;
                                    sum++;
                                }
                            }
                        }
                        total -= output[i, j, dimension].Value;
                        sum--;
                        if (Math.Abs(output[i, j, dimension].Value - (total / sum)) > threshold)
                        {
                            output[i, j, dimension] = (byte)(total / sum);
                        }
                    }
                }
            }
            return output.ToImage();
        }

        private WriteableBitmap ConvertToGrayscale(BitmapSource image)
        {
            WriteableBitmap bitmap = new WriteableBitmap(image);
            byte[] bytes = new byte[bitmap.PixelHeight * bitmap.PixelWidth * bitmap.Format.BitsPerPixel / 8];
            bitmap.CopyPixels(bytes, bitmap.PixelWidth * bitmap.Format.BitsPerPixel / 8, 0);

            byte[] converted = new byte[bitmap.PixelHeight * bitmap.PixelWidth];
            int index = 0;
            for (int i = 0; i < bitmap.PixelHeight; i++)
            {
                for (int j = 0; j < bitmap.PixelWidth; j++)
                {
                    //byte a = bytes[(i * bitmap.PixelWidth + j) * 4];
                    byte r = bytes[(i * bitmap.PixelWidth + j) * 4 + 1];
                    byte g = bytes[(i * bitmap.PixelWidth + j) * 4 + 2];
                    byte b = bytes[(i * bitmap.PixelWidth + j) * 4 + 3];
                    converted[index++] = (byte)((0.299 * r + 0.587 * g + 0.114 * b));
                }
            }
            WriteableBitmap bitmap2 = new WriteableBitmap(bitmap.PixelWidth, bitmap.PixelHeight, bitmap.DpiX, bitmap.DpiY, PixelFormats.Gray8, bitmap.Palette);
            bitmap2.WritePixels(new System.Windows.Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight), converted, bitmap.PixelWidth, 0);
            return bitmap2;
        }

        private BitmapSource LinearFilter(BitmapSource image, int size, int[,] matrix = null, int sum = 0)
        {
            ImageHelper helper = new ImageHelper(image);
            ImageHelper output = new ImageHelper(image);
            if (matrix == null)
            {
                matrix = new int[2 * size + 1, 2 * size + 1];
                for (int i = 0; i < 2 * size + 1; i++)
                {
                    for (int j = 0; j < 2 * size + 1; j++)
                    {
                        matrix[i, j] = 1;
                    }
                }
            }
            if (sum == 0)
                foreach (var item in matrix)
                {
                    sum += item;
                }
            int total;
            var dimensions = image.Format.BitsPerPixel / 8;
            for (int dimension = 0; dimension < dimensions; dimension++)
            {
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        total = 0;
                        for (int u = -size; u <= size; u++)
                        {
                            for (int v = -size; v <= size; v++)
                            {
                                if (helper[i - u, j - v, dimension] != null)
                                {
                                    total += helper[i - u, j - v, dimension].Value * matrix[u + size, v + size];
                                }
                            }
                        }
                        output[i, j, dimension] = (byte)(Math.Abs(total) / sum);
                    }
                }
            }
            return output.ToImage();
        }

        private BitmapSource MaxFilter(BitmapSource image, int size = 1)
        {
            ImageHelper helper = new ImageHelper(image);
            ImageHelper output = new ImageHelper(image);
            List<byte> list = new List<byte>();
            var dimensions = image.Format.BitsPerPixel / 8;
            for (int dimension = 0; dimension < dimensions; dimension++)
            {
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        list.Clear();
                        for (int u = -size; u <= size; u++)
                        {
                            for (int v = -size; v <= size; v++)
                            {
                                if (helper[i - u, j - v, dimension] != null)
                                    list.Add(helper[i - u, j - v, dimension] ?? 0);
                            }
                        }
                        output[i, j, dimension] = list.Max();
                    }
                }
            }
            return output.ToImage();
        }

        private BitmapSource MedianFilter(BitmapSource image, int size = 1)
        {
            ImageHelper helper = new ImageHelper(image);
            ImageHelper output = new ImageHelper(image);
            List<byte> list = new List<byte>();
            var dimensions = image.Format.BitsPerPixel / 8;
            for (int dimension = 0; dimension < dimensions; dimension++)
            {
                for (int i = 0; i < image.PixelHeight; i++)
                {
                    for (int j = 0; j < image.PixelWidth; j++)
                    {
                        list.Clear();
                        for (int u = -size; u <= size; u++)
                        {
                            for (int v = -size; v <= size; v++)
                            {
                                if (helper[i - u, j - v, dimension] != null)
                                    list.Add(helper[i - u, j - v, dimension] ?? 0);
                            }
                        }
                        list.Sort();
                        output[i, j, dimension] = list[list.Count / 2];
                    }
                }
            }
            return output.ToImage();
        }
    }
}